import React, { useEffect, useState, useCallback, useRef, useImperativeHandle, forwardRef, useMemo } from 'react';
import useParamFnFreeze from '@y.dsh/hooks/lib/useParamFnFreeze';
import usePassiveSupported from '@y.dsh/hooks/lib/usePassiveSupported';
import { slideTo } from '@y.dsh/utils/lib/slideTo';
import { inclinedAngle, preventDefault, getClientPos, haveToWindow, getBrowserVersion } from '@y.dsh/utils/lib/utils';
import Down from './Down';
import Up from './Up';
import Side from './Side';
import { Props, ScrollMethods, DownStatus, DownMethods, UpStatus } from '../types';
import './styles/styles.scss';
import './Icon/styles/styles.scss';

export type SetPrevent = () => void;

// 拖拽需要的尺寸
export interface Pos {
  posX: number
  posY: number
  width: number
  height: number
}

function ScrollView(props: Props, ref: any): JSX.Element {
  const { children, down = {}, up = {}, lazy = {}, side = {} } = props;

  const passiveSupported = usePassiveSupported(); // 检测是否支持{ passive: false }
  const eventOptions = useMemo(() => passiveSupported ? { passive: false } : false, [passiveSupported]);
  const firstEntry = useRef(true); // 是否初次进入
  const view = useRef<HTMLDivElement | null>(null); // 最外层盒子容器
  /**
   * @name 公共状态，此时下拉刷新与上拉加载都不触发
   * 有时候子节点需要做操作 但又不想触发下拉刷新与上拉加载
   */
  const childrenNotAllowDown = useRef(false);
  /**
   * @name 下拉状态 注：所有的状态都会回到0
   * @param 无操作 0
   * @param 下拉-未达到刷新条件 1
   * @param 下拉-达到刷新条件 2
   * @param 释放-回弹至刷新中 3
   * @param 刷新中 4
   * @param 刷新成功 5
   * @param 释放-回弹至初始位置 6
   * @param 刷新失败 7
   */
  const [downStatus, setDownStatus] = useState<DownStatus>(0);
  const downState = useParamFnFreeze({ // 冻结值 避免频繁渲染
    downStatus,
  });
  const getDownState = useRef(() => downState.current); // 为了在副作用函数上少写依赖
  /**
    * @name 上拉状态
    * @param 无操作 0
    * @param 正在加载 1
    * @param 加载成功 2
    * @param 加载失败 3
    * @param 到底了 4
    */
  const [upStatus, setUpStatus] = useState<UpStatus>(0);
  const upState = useParamFnFreeze({ // 冻结值 避免频繁渲染
    upStatus,
  });
  const getUpState = useRef(() => upState.current); // 为了在副作用函数上少写依赖


  // 下拉
  const {
    enable: downEnable = true,
    offset: downOffset = 80,
    outOffsetRate: downOutOffsetRate = 0.2,
    successStay: downSuccessStay = 800,
    refreshing: downRefreshing,
    custom: DownCustom
  } = down;
  const downParams = useParamFnFreeze({ // 冻结值 避免频繁渲染
    downEnable,
    downOffset,
    downOutOffsetRate,
    downSuccessStay,
    downRefreshing
  });
  const getDownParams = useRef(() => downParams.current); // 为了在副作用函数上少写依赖
  const downMoveData = useRef({ // 各个时期触摸点的信息
    start: { x: 0, y: 0 },
    prev: { x: 0, y: 0 },
    curve: { x: 0, y: 0 }
  });
  const downRef = useRef<DownMethods>(); // 下拉刷新盒子
  const downHeight = useRef(0); // 下拉刷新盒子容器高度
  const downAllow = useRef(false); // 是否允许执行下拉功能 用来做一些额外的限制
  const downTimer = useRef<SetPrevent | null>(null); // 下拉释放后回弹的定时器
  const downClearTimer = useRef(() => {
    if (downTimer.current) {
      downTimer.current();
      downTimer.current = null;
    }
  });
  const downTouchmoveCount = useRef(0); // 下拉手指移动次数
  const downEventTarget = useRef<any>(null); // 已绑定事件的对象 用于后面的手指按下时解绑前面的手指

  // 下拉刷新逻辑
  const toRefresh = useRef(() => { // 前往刷新中
    setDownStatus(3);
    downClearTimer.current();
    downTimer.current = slideTo({
      startPos: downHeight.current,
      endPos: downOffset,
      chunkCallback(nextScrollTop) {
        // 不断靠近
        if (downRef.current) {
          downHeight.current = nextScrollTop;
          downRef.current.setHeight(nextScrollTop);
        }
      },
      completeCallback(endPos) {
        // 到达 开始执行刷新
        if (downRef.current) {
          downHeight.current = endPos;
          downRef.current.setHeight(endPos);
        }
        setDownStatus(4);
        const { downRefreshing } = getDownParams.current();
        if (downRefreshing) {
          downRefreshing();
        }
      }
    });
  });
  const toInitial = useRef(() => { // 前往初始位置0
    setDownStatus(6);
    downClearTimer.current();
    downTimer.current = slideTo({
      startPos: downHeight.current,
      endPos: 0,
      chunkCallback(nextScrollTop) {
        // 不断靠近
        if (downRef.current) {
          downHeight.current = nextScrollTop;
          downRef.current.setHeight(nextScrollTop);
        }
      },
      completeCallback(endPos) {
        // 到达原始位置
        if (downRef.current) {
          downHeight.current = endPos;
          downRef.current.setHeight(endPos);
        }
        setDownStatus(0);
      }
    });
  });
  const downComplete = useCallback<(p: { success: boolean }) => void>(({ success }) => { // 刷新完成
    const { downSuccessStay } = getDownParams.current();
    if (success) {
      setDownStatus(5); // 刷新成功
    } else {
      setDownStatus(7); // 刷新失败
    }
    // 短暂驻留功能
    if (downSuccessStay > 0) {
      setTimeout(() => { // 本定时器等待是不允许做其它操作 因此不需要清除
        toInitial.current();
      }, downSuccessStay);
    } else {
      toInitial.current();
    }
  }, []);

  // 事件
  const customDownMove = useCallback((detail) => {
    if (haveToWindow() && window.CustomEvent) {
      const event = new window.CustomEvent("custom-down-touchmove", {
        detail
      });
      view.current?.dispatchEvent(event);
    }
  }, []);
  const customDownStop = useCallback((detail) => {
    if (haveToWindow() && window.CustomEvent) {
      const event = new window.CustomEvent("custom-down-touchend", {
        detail
      });
      view.current?.dispatchEvent(event);
    }
  }, []);
  const handleDownMove = useCallback((e) => {
    const custom = e.type.indexOf('custom-') > -1; // 自定义事件
    const event = getClientPos(custom ? e.detail : e);
    downMoveData.current.curve = { x: event.clientX, y: event.clientY }; // 当前点的位置
    const { downOffset, downOutOffsetRate } = getDownParams.current();
    const { downStatus } = getDownState.current();
    const { upStatus } = getUpState.current();
    if (childrenNotAllowDown.current === false) {
      downTouchmoveCount.current += 1; // 滑动次数+1
      if (downTouchmoveCount.current === 1) {
        const angle = inclinedAngle(downMoveData.current.start, downMoveData.current.curve);
        if (!((angle > 45 && angle < 135) || (angle > 225 && angle < 315))) { // 0 - 360
          downAllow.current = false; // 不符合角度将不允许下拉
        }
      }
      if (
        view.current // 有最外层的视图容器
        && upStatus !== 1 // 不在上拉加载状态
        && downAllow.current // 允许执行刷新
        && [0, 1, 2, 3, 6].indexOf(downStatus) > -1 // 这些状态允许进入
      ) {
        if ([3, 6].indexOf(downStatus) > -1) { // 在回弹的过程中 并且开启了回弹定时器
          downClearTimer.current();
        }
        const scrollTop = view.current.scrollTop; // 视图容器内滚动高度
        let changeHeight = downMoveData.current.curve.y - downMoveData.current.prev.y; //  改变的高度
        if (
          (scrollTop <= 0 && changeHeight >= 0) // 在起始位置往下滑
          || ([1, 2].indexOf(downStatus) > -1 && changeHeight < 0) // 在下拉的时候再往回推
        ) {
          preventDefault(custom ? e.detail : e); // 许多浏览器有自己的下拉默认事件 需要阻止
          if (changeHeight < 0) { // 往回推
            if (downHeight.current < 1) { // 回推到不足1时 直接置为0 避免bug
              downHeight.current = 0;
            } else {
              downHeight.current += changeHeight; // 计算盒子的高度
            }
          } else { // 下拉
            if (downHeight.current >= downOffset) { // 临界值的判断 设置摩擦阻力
              changeHeight *= downOutOffsetRate;
            }
            downHeight.current += changeHeight;
          }
          if (downRef.current) { // 设置down框的高度 以显示出来
            downRef.current.setHeight(downHeight.current); // 调用函数设置高度
          }
          if (downHeight.current >= downOffset) { // 临界值的判断 判断计算好后的值符合何种状态
            setDownStatus(2); // 置为下拉可刷新的状态
          } else {
            setDownStatus(1); // 置为下拉不可刷新的状态
          }
        }
      }
    }
    downMoveData.current.prev = downMoveData.current.curve; // 结束后更新位置
  }, []);
  const handleDownEnd = useCallback((e) => {
    const custom = e.type.indexOf('custom-') > -1; // 自定义事件
    if (getBrowserVersion() === 'IE') {
      document.body.classList.remove('easy-prohibit-to-choose');
    }
    const { downStatus } = getDownState.current();
    if (downStatus === 1 || (downStatus === 6 && !downTimer.current)) {
      // 未达到刷新条件 && 回弹的时候如果被打断 并且没有改变成其它状态的情况下 需要重启回弹
      toInitial.current();
    } else if (downStatus === 2 || (downStatus === 3 && !downTimer.current)) {
      // 达到刷新条件 && 回弹的时候如果被打断 并且没有改变成其它状态的情况下 需要重启回弹
      toRefresh.current();
    }
    if (haveToWindow()) {
      window.removeEventListener('mousemove', handleDownMove);
      window.removeEventListener('mouseup', handleDownEnd);

      if (custom) { // 自定义事件需要解除
        e.detail.target.removeEventListener('touchmove', customDownMove);
        e.detail.target.removeEventListener('touchend', customDownStop);
        e.detail.target.removeEventListener('touchcancel', customDownStop);
        downEventTarget.current = null;
      }
    }
  }, [customDownMove, customDownStop, handleDownMove]);
  const handleDownStart = useCallback((e: TouchEvent | MouseEvent) => {
    if (getBrowserVersion() === 'IE') {
      document.body.classList.add('easy-prohibit-to-choose'); // 阻止默认事件会导致无法默认滚动 因此使用本方法禁止选中文字
    }
    const event = getClientPos(e);
    downMoveData.current.start = { x: event.clientX, y: event.clientY }; // 记录手指起始位置
    downMoveData.current.prev = { x: event.clientX, y: event.clientY }; // 记录手指上一次位置
    if (childrenNotAllowDown.current === false) {
      downAllow.current = true;
      downTouchmoveCount.current = 0; // 滑动次数重新计算

      if (haveToWindow()) {
        window.removeEventListener('mousemove', handleDownMove);
        window.removeEventListener('mouseup', handleDownEnd);
        window.addEventListener('mousemove', handleDownMove);
        window.addEventListener('mouseup', handleDownEnd);

        if (e.target) {
          if (downEventTarget.current && downEventTarget.current !== e.target) { // 后面的手指按下 并且按在不同的元素上 把上一个手指的事件删除
            downEventTarget.current.removeEventListener('touchmove', customDownMove);
            downEventTarget.current.removeEventListener('touchend', customDownStop);
            downEventTarget.current.removeEventListener('touchcancel', customDownStop);
          }
          if (!downEventTarget.current || downEventTarget.current !== e.target) { // 第一指 或者后续按在其它元素上 重新绑定
            e.target.addEventListener('touchmove', customDownMove, eventOptions);
            e.target.addEventListener('touchend', customDownStop);
            e.target.addEventListener('touchcancel', customDownStop);
          }
          downEventTarget.current = e.target;
        }
      }
    }
  }, [customDownMove, customDownStop, eventOptions, handleDownEnd, handleDownMove]);

  // 事件绑定/解绑处理
  // 绑定流程
  // 1. true -> false 不在下拉状态 直接解绑
  // 2. true -> false 在下拉状态 延迟解绑
  // 3. false -> true 已解绑|未绑定 直接绑定
  // 4. false -> true 在延迟解绑状态 取消延迟解绑
  const downEventIsBind = useRef(false); // 当前是否绑定
  const downEventDelayDestroy = useRef(false); // 绑定需要延迟解绑

  const downEventBind = useCallback(() => { // 绑定
    if (!downEventIsBind.current && view.current) { // 已解绑|未绑定 直接绑定
      downEventIsBind.current = true;
      view.current.addEventListener('mousedown', handleDownStart);
      view.current.addEventListener('touchstart', handleDownStart, eventOptions);
      view.current.addEventListener('custom-down-touchmove', handleDownMove);
      view.current.addEventListener('custom-down-touchend', handleDownEnd);
    } else if (downEventDelayDestroy.current) { // 在延迟解绑状态 取消延迟解绑
      downEventDelayDestroy.current = false;
    }
  }, [eventOptions, handleDownEnd, handleDownMove, handleDownStart]);
  const downEventDestroy = useCallback(() => { // 销毁
    const { downStatus } = getDownState.current();
    if (downEventIsBind.current && view.current) {
      if (downStatus === 0) { // 不在下拉状态 直接解绑
        downEventIsBind.current = false;
        downEventDelayDestroy.current = false; // 已经解绑 需要重置值
        view.current.removeEventListener('mousedown', handleDownStart);
        view.current.removeEventListener('touchstart', handleDownStart);
        view.current.removeEventListener('custom-down-touchmove', handleDownMove);
        view.current.removeEventListener('custom-down-touchend', handleDownEnd);
      } else if (downEventDelayDestroy.current === false) { //  在下拉状态 延迟解绑
        downEventDelayDestroy.current = true;
      }
    }
  }, [handleDownEnd, handleDownMove, handleDownStart]);
  // 处理绑定与取消
  useEffect(() => {
    if (downEnable) { // 需要绑定
      downEventBind();
    }
    return downEventDestroy
  }, [downEnable, downEventBind, downEventDestroy]);
  // 下拉刷新是一个持续的过程 在这个过程中如果禁用需要延迟执行
  useEffect(() => {
    if (downEnable === false && downStatus === 0 && downEventDelayDestroy.current) {
      downEventDestroy();
    }
  }, [downEnable, downEventDestroy, downStatus]);


  // 上拉加载逻辑
  const {
    enable: upEnable = true,
    offset: upOffset = 100,
    renderDom: upRenderDom = true,
    successStay: upSuccessStay = 600,
    loading: upLoading,
    custom: UpCustom
  } = up;
  const upParams = useParamFnFreeze({ // 冻结值 避免频繁渲染
    upEnable,
    upOffset,
    upRenderDom,
    upSuccessStay,
    upLoading
  });
  const getUpParams = useRef(() => upParams.current); // 为了在副作用函数上少写依赖
  const upSuccessTimer = useRef<NodeJS.Timeout>(); // 上拉加载成功定时器
  const upScrollMoveData = useRef({ prev: 0, curve: 0 }); // 滚动条位置信息 适用于scroll事件
  const upMoveData = useRef({ // 各个时期触摸点的信息 适用于到底了上拉
    start: { x: 0, y: 0 },
    prev: { x: 0, y: 0 },
    curve: { x: 0, y: 0 }
  });
  const upEventTarget = useRef<any>(null); // 已绑定事件的对象 用于后面的手指按下时解绑前面的手指

  const loadComplete = useCallback(({ success, end }) => { // 加载完成 外部调用
    const { upSuccessStay } = getUpParams.current();
    if (end) {
      setUpStatus(4); // 到底了
    } else {
      if (success) {
        setUpStatus(2); // 获取成功
        if (upSuccessStay > 0) { // 上拉加载只有成功才会短暂驻留
          upSuccessTimer.current = setTimeout(() => {
            setUpStatus(0);
          }, upSuccessStay);
        } else {
          setUpStatus(0);
        }
      } else {
        setUpStatus(3); // 获取失败
      }
    }
  }, []);

  // 事件
  const handleUpScroll = useCallback(() => { // 滚动条滚动时触发
    const { downStatus } = getDownState.current();
    const { upOffset, upLoading } = getUpParams.current();
    const { upStatus } = getUpState.current();
    if (view.current) {
      upScrollMoveData.current.curve = view.current.scrollTop; // 当前滚动位置
      const { prev, curve } = upScrollMoveData.current;
      if (
        downStatus === 0 // 不在下拉中
        && [0, 2, 3].indexOf(upStatus) > -1 // 无状态 加载成功 加载失败
        && curve > prev // 向下滚动可加载
      ) {
        const showHeight = curve + view.current.clientHeight; // 卷去的高度 + 盒子高度
        const remain = view.current.scrollHeight - showHeight; // 距离底部的距离
        if (remain < upOffset) { // 到达了临界点 触发加载
          if (upStatus === 2) { // 如果是成功状态 需要清除一下定时器 因为可能正在提示成功中
            clearTimeout(Number(upSuccessTimer.current));
          }
          setUpStatus(1);
          if (upLoading) {
            upLoading();
          }
        }
      }
      upScrollMoveData.current.prev = curve; // 当前滚动位置
    }
  }, []);
  const customUpMove = useCallback((detail) => {
    if (haveToWindow() && window.CustomEvent) {
      const event = new window.CustomEvent("custom-up-touchmove", {
        detail
      });
      view.current?.dispatchEvent(event);
    }
  }, []);
  const customUpStop = useCallback((detail) => {
    if (haveToWindow() && window.CustomEvent) {
      const event = new window.CustomEvent("custom-up-touchend", {
        detail
      });
      view.current?.dispatchEvent(event);
    }
  }, []);
  const handleUpMove = useCallback((e) => {
    const custom = e.type.indexOf('custom-') > -1; // 自定义事件 
    const event = getClientPos(custom ? e.detail : e);
    const { downStatus } = getDownState.current();
    const { upLoading } = getUpParams.current();
    const { upStatus } = getUpState.current();
    upMoveData.current.curve = { x: event.clientX, y: event.clientY }; // 当前点的位置
    if (childrenNotAllowDown.current === false) {
      if (
        view.current
        && downStatus === 0 // 不在下拉中
        && [0, 2, 3].indexOf(upStatus) > -1 // 无状态 加载成功 加载失败
        && upMoveData.current.curve.y < upMoveData.current.prev.y // 向上拉
      ) {
        const remain = view.current.scrollHeight - (view.current.scrollTop + view.current.clientHeight); // 距离底部的距离
        if (remain === 0) { // 触底了 触发加载 解决触底时继续上拉无法触发scroll事件的问题
          const angle = inclinedAngle(upMoveData.current.start, upMoveData.current.curve);
          if (angle > 225 && angle < 315) { // 此处与下拉刷新不同 始终以当前点与最初点形成夹角
            if (upStatus === 2) { // 如果是成功状态 需要清除一下定时器 因为可能正在提示成功中
              clearTimeout(Number(upSuccessTimer.current));
            }
            setUpStatus(1);
            if (upLoading) {
              upLoading();
            }
          }
        }
      }
    }
    upMoveData.current.prev = upMoveData.current.curve; // 结束后更新位置
  }, []);
  const handleUpEnd = useCallback((e) => {
    const custom = e.type.indexOf('custom-') > -1; // 自定义事件
    if (haveToWindow()) {
      window.removeEventListener('mousemove', handleUpMove);
      window.removeEventListener('mouseup', handleUpEnd);

      if (custom) { // 自定义事件需要解除
        e.detail.target.removeEventListener('touchmove', customUpMove);
        e.detail.target.removeEventListener('touchend', customUpStop);
        e.detail.target.removeEventListener('touchcancel', customUpStop);
        upEventTarget.current = null;
      }
    }
  }, [customUpMove, customUpStop, handleUpMove]);
  const handleUpStart = useCallback((e: TouchEvent | MouseEvent) => {
    const event = getClientPos(e);
    upMoveData.current.start = { x: event.clientX, y: event.clientY }; // 记录手指起始位置
    upMoveData.current.prev = { x: event.clientX, y: event.clientY }; // 记录手指上一次位置
    if (childrenNotAllowDown.current === false && view.current) {
      const { scrollTop, scrollHeight, clientHeight } = view.current;
      upScrollMoveData.current.curve = scrollTop; // 当前滚动位置
      upScrollMoveData.current.prev = scrollTop; // 当前滚动位置
      const remain = scrollHeight - scrollTop - clientHeight;  // 距离底部的距离
      if (remain === 0) { // 触底了 绑定滑动事件 解决触底时继续上拉无法触发scroll事件的问题
        if (haveToWindow()) {
          window.removeEventListener('mousemove', handleUpMove);
          window.removeEventListener('mouseup', handleUpEnd);
          window.addEventListener('mousemove', handleUpMove);
          window.addEventListener('mouseup', handleUpEnd);

          if (e.target) {
            if (upEventTarget.current && upEventTarget.current !== e.target) { // 后面的手指按下 并且按在不同的元素上 把上一个手指的事件删除
              upEventTarget.current.removeEventListener('touchmove', customUpMove);
              upEventTarget.current.removeEventListener('touchend', customUpStop);
              upEventTarget.current.removeEventListener('touchcancel', customUpStop);
            }
            if (!upEventTarget.current || upEventTarget.current !== e.target) { // 第一指 或者后续按在其它元素上 重新绑定
              e.target.addEventListener('touchmove', customUpMove, eventOptions);
              e.target.addEventListener('touchend', customUpStop);
              e.target.addEventListener('touchcancel', customUpStop);
            }
            upEventTarget.current = e.target;
          }
        }
      }
    }
  }, [customUpMove, customUpStop, eventOptions, handleUpEnd, handleUpMove]);
  // 事件绑定/解绑处理
  const upEventIsBind = useRef(false); // 当前是否绑定
  // 绑定
  const upEventBind = useCallback(() => {
    if (!upEventIsBind.current && view.current) { // 当前未绑定
      upEventIsBind.current = true;
      view.current.addEventListener('scroll', handleUpScroll);
      view.current.addEventListener('mousedown', handleUpStart);
      view.current.addEventListener('touchstart', handleUpStart, eventOptions);
      view.current.addEventListener('custom-up-touchmove', handleUpMove);
      view.current.addEventListener('custom-up-touchend', handleUpEnd);
    }
  }, [eventOptions, handleUpEnd, handleUpMove, handleUpScroll, handleUpStart]);
  // 解绑
  const upEventDestroy = useCallback(() => {
    if (upEventIsBind.current && view.current) { // 当前已绑定
      upEventIsBind.current = false;
      view.current.removeEventListener('scroll', handleUpScroll)
      view.current.removeEventListener('mousedown', handleUpStart);
      view.current.removeEventListener('touchstart', handleUpStart);
      view.current.removeEventListener('custom-up-touchmove', handleUpMove);
      view.current.removeEventListener('custom-up-touchend', handleUpMove);
    }
  }, [handleUpMove, handleUpScroll, handleUpStart]);
  useEffect(() => {
    if (upEnable) { // 需要绑定
      upEventBind();
    }
    return upEventDestroy
  }, [upEnable, upEventBind, upEventDestroy]);


  // 懒加载 
  const {
    enable: lazyEnable = true,
    offset: lazyOffset = 100,
    srcAttribute: lazySrcAttribute = 'data-lazy',
  } = lazy;
  const lazyParams = useParamFnFreeze({ // 冻结值 避免频繁渲染
    lazyEnable,
    lazyOffset,
    lazySrcAttribute,
  });
  const getLazyParams = useRef(() => lazyParams.current); // 为了在副作用函数上少写依赖
  const lazyEventIsBind = useRef(false); // 是否绑定了事件
  const handleLazyScroll = useCallback(() => {
    if (view.current) {
      // 显示图片的高度
      const { lazySrcAttribute, lazyOffset } = getLazyParams.current();
      const showHeight = view.current.clientHeight + lazyOffset; // 图片距离屏幕顶部的距离小于这个数 就需要显示
      const lazyImg = document.querySelectorAll(`img[${lazySrcAttribute}]`); // 所有使用了懒加载的IMG标签
      for (let i = 0; i < lazyImg.length; i += 1) {
        const img = lazyImg[i];
        if (img.getBoundingClientRect().top <= showHeight) {
          img.setAttribute('src', img.getAttribute(lazySrcAttribute) || '');
          img.removeAttribute(lazySrcAttribute);
        }
      }
    }
  }, []);
  const lazyEventBind = useCallback(() => {
    if (!lazyEventIsBind.current && view.current) {
      lazyEventIsBind.current = true;
      view.current.addEventListener('scroll', handleLazyScroll);
    }
  }, [handleLazyScroll]);
  const lazyEventDestroy = useCallback(() => {
    if (lazyEventIsBind.current && view.current) {
      lazyEventIsBind.current = false;
      view.current.removeEventListener('scroll', handleLazyScroll);
    }
  }, [handleLazyScroll]);
  useEffect(() => {
    if (lazyEnable) {
      handleLazyScroll();
      lazyEventBind();
    }
    return lazyEventDestroy
  }, [lazyEnable, handleLazyScroll, lazyEventBind, lazyEventDestroy]);


  // 暴露的事件
  useImperativeHandle<any, ScrollMethods>(ref, () => ({
    downComplete,
    loadComplete
  }));

  // 下拉节点的参数
  const downProps = useMemo(() => ({
    ref: downRef,
    status: downStatus,
    offset: downOffset,
    outOffsetRate: downOutOffsetRate
  }), [downOffset, downOutOffsetRate, downStatus]);
  const DownMemo = useMemo(() => {
    return forwardRef(DownCustom ? DownCustom : Down)
  }, [DownCustom]);

  // 上拉节点参数
  const upProps = useMemo(() => ({
    status: upStatus
  }), [upStatus]);
  const UpMemo = useMemo(() => {
    return UpCustom ? UpCustom : Up
  }, [UpCustom]);

  // 必须写在最后，将初次进入的标识置为否
  useEffect(() => {
    firstEntry.current = false;
  }, []);

  return (
    <div
      className="easy-scroll-view"
      ref={view}
    >
      <DownMemo {...downProps} />
      <div className="easy-scroll-body">
        {children}
      </div>
      {side.button ? <Side {...side} childrenNotAllowDown={childrenNotAllowDown} /> : null}
      {upEnable && upRenderDom ? <UpMemo {...upProps} /> : null}
    </div>
  )
}

export default forwardRef(ScrollView)
